﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Enlightener.Engine.Structs__Enums___Interfaces;
using Enlightener.Engine.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Enlightener.Engine.Deobfuscator.Tasks._1_7
{
    public class CtorProxyRemover : DeobfuscationTask
    {
        private Assembly _asm;

        public class ProxyEntry
        {
            public MethodDefinition Caller;
            public Instruction Source;
            public TypeDefinition ProxyType;
        }

        public CtorProxyRemover(AssemblyDefinition asmDef)
            : base(asmDef)
        {
            _asm = Assembly.LoadFile(DeobfuscatorContext.InPath);
        }

        [DeobfuscationPhase(1, "Construction proxy entries")]
        public static bool Phase1()
        {
            var proxyEntries = new List<ProxyEntry>();

            foreach(var mDef in AsmDef.FindMethods(m => true))
            {
                if (!mDef.HasBody)
                    continue;

                foreach(var instr in mDef.Body.Instructions)
                    if(instr.OpCode == OpCodes.Call && IsProxyType((instr.Operand as MethodReference).Resolve().DeclaringType))
                    {
                        var tmpEntry = new ProxyEntry
                                           {
                                               Source = instr,
                                               Caller = mDef,
                                               ProxyType = (instr.Operand as MethodReference).Resolve().DeclaringType
                                           };

                        proxyEntries.Add(tmpEntry);
                    }
            }

            if(proxyEntries.Count == 0)
            {
                PhaseError = new PhaseError
                                 {
                                     Level = PhaseError.ErrorLevel.Minor,
                                     Message = "No constructor proxies?"
                                 };

                EmergencyCancel = true;
                return true;
            }

            PhaseParam = proxyEntries;
            Logger.VSLog(string.Format("{0} proxy entries constructed...", proxyEntries.Count));

            return true;
        }

        [DeobfuscationPhase(2, "Replace proxies with resolved members")]
        public static bool Phase2()
        {
            var proxyEntries = PhaseParam as List<ProxyEntry>;

            foreach(var proxy in proxyEntries)
            {
                var resolvedMember = proxy.ProxyType.Methods[2].ReturnType;
                var ilProc = proxy.Caller.Body.GetILProcessor();

                ilProc.Replace(proxy.Source, ilProc.Create(proxy.Source.OpCode, resolvedMember));
            }

            return true;
        }

        private static bool IsProxyType(TypeDefinition typeDef)
        {
            if (typeDef.BaseType == null)
                return false;

            //Weak
            return typeDef.BaseType.Name == "MulticastDelegate";
        }
    }
}
